package ReflectionLearn;



import org.junit.jupiter.api.Test;

import java.lang.reflect.*;

/**
 * @author Lenl
 * @version v1.0
 * @create 2021-06-06 22:50
 * @description
 */
public class ReflectionTest {
    //反射之前对于Person的操作
    @Test
    public void test1(){
        //1.创建Person类的对象
        Person p1=new Person("Tom",12);
        //2.通过对象，调用其内部的属性、方法
        p1.age=10;
        System.out.println(p1.toString());
        p1.show();

        //在Person类外部不能通过Person的对象调用其内部私有结构
        //如name、showNation()方法
    }

    //反射以后，对于Person的操作
    @Test
    public void test2() throws Exception {
        //Class反射的源头
        Class clazz=Person.class;
        //1.通过反射，创建Person类的对象
        Constructor c= clazz.getConstructor(String.class,int.class);
        Person p= (Person) c.newInstance("Tom",12);
        //2.通过反射调用对象指定的属性、方法
        //调用属性
        Field age= clazz.getDeclaredField("age");
        age.set(p,10);
        System.out.println(p.toString());
        //调用方法
        Method show= clazz.getDeclaredMethod("show");
        show.invoke(p);
        System.out.println("************调用私有结构**************");

        //通过反射，可以调用类的私有结构：如构造器、属性、方法
        Constructor c1=clazz.getDeclaredConstructor(String.class);
        c1.setAccessible(true);
        Person p1=(Person)c1.newInstance("Jerry");
        System.out.println(p1.toString());
        //调用私有属性
        Field name=clazz.getDeclaredField("name");
        name.setAccessible(true);
        name.set(p1,"韩梅梅");
        System.out.println(p1);
        //调用私有方法
        Method showNation=clazz.getDeclaredMethod("showNation", String.class);
        showNation.setAccessible(true);
        String nation=(String) showNation.invoke(p1,"中国");
        System.out.println(nation);
    }
    //疑问1：通过直接new或反射都能调用公共的结构，开发中用哪个
        //一般建议用直接new，
        //什么时候用反射：反射的特征：动态性

    //疑问？：反射机制与面向对象的封装性是否矛盾？如何看待两个技术？
      //不矛盾。

    //万事万物皆对象？ 对象.xxx，File,URL,反射、前端、数据库
    /**
     * 关于java.lang.Class类的理解
     * 1.类的加载过程：
     *  程序经过javac.exe命令后，会生成一个或多个字节码文件（.class结尾），
     *  接着我们使用java.exe对某个字节码文件进行解释运行。相当于将某个字节码文件
     *  加载到内存中。此过程就称为类的加载。加载到内存中的类，我们就称之为运行时类，此
     *  运行时类，就作为Class的一个实例。
     * 2.换句话说，Class的示例就对应着一个运行时类
     * 3.加载到内存中的运行时类会缓存一定的时间。在此时间内，我们可以通过不同的方式来获取此运行时类
     *
     */
    //获取Class实例的方式（前三种方式需要掌握）
    @Test
    public void test3() throws ClassNotFoundException {
        //方式一：
        Class<Person> clazz1=Person.class;
        System.out.println(clazz1);
        //方式二：通过运行时类的对象
        Person p1=new Person();
        Class clazz2=p1.getClass();//这里不能加泛型
        System.out.println(clazz2);
        //方式三：调用Class的静态方法：forName(String classPath)//需要写全路径 ！用的最多！
        Class clazz3= Class.forName("ReflectionLearn.Person");
        System.out.println(clazz3);

        System.out.println(clazz1==clazz2);//true
        System.out.println(clazz2==clazz3);//true

        //方式四：类的加载器：ClassLoader (了解)
        ClassLoader loader= ReflectionTest.class.getClassLoader();
        Class clazz4=loader.loadClass("ReflectionLearn.Person");
        System.out.println(clazz4);
        System.out.println(clazz1==clazz4);

    }


}
